from django.shortcuts import render
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer
from django_redis import get_redis_connection
from goods.models import SKU
import base64
import pickle

# Create your views here.
# sku_id,count,selected
# key = cart_user_id区分用户

# redis尽量少占用资源实现功能
# hash
# cart_user_id:{sku_id:count}
# cart_1:{sku_id:count}
# set
# 是否选中，只把选中的商品id记录在set中
# cart_selected_id:sku_id
# {sku_id:selected}
# cart_selected_id:1


# cookie
# cart:{}
# sku_id:{'count':10,'selected':0}


"""
增加数据
1.先提交数据,后端要接收数据
2.接收数据之后对数据进行校验
3.获取到校验数据之后,根据用户的登录状态进行处理
4.登录用户保存在redis中
5.非登录用户保存在cookie中
"""""
class CartView(APIView):
    """
    POST    /carts/      实现添加购物车的功能
    如果用户没有传递token或者传递的是正确的token都可以执行POST方法
    如果用户伪造token,不能添加到购物车,需要将对用户的校验先去掉
    """""

    # 重写对用户的验证方法,就可以让伪造的token进入到添加购物车的功能
    def perform_authentication(self, request):
        pass

    # 添加购物车模块
    def post(self, request):
        # 因为重写了perform_authentication所以获取用户信息的时候
        # 对于伪造的用户信息获取可能会产生异常,所以要捕获异常
        # 1.先提交数据,后端要接收数据(sku_id,count,selected)
        # 2.接收数据之后对数据进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 3.获取到校验数据之后,根据用户的登录状态进行处理
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except Exception:
            user = None

        # is_authenticated判断用户是否为登录用户
        if user and user.is_authenticated:
            # 4.登录用户保存在redis中
            # 4.1连接redis
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 4.2添加数据
            # sku_id:count hash
            # 添加redis的时候 有可能之前已经存在了该商品
            # redis_conn.hset('cart_%s' % user.id, sku_id, count)
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # selected:{sku_id} set
            if selected:
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            # 4.3返回响应
            return Response(serializer.data)
        else:
            # 5.非登录用户保存在cookie中
            # 5.1查看cookie中是否存在购物车数据
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart:
                decode = base64.b64decode(cookie_cart)
                cart = pickle.loads(decode)
            else:
                cart = {}
            # 5.2更新数据,判断商品是否存在,存在则累加个数,不存在则直接添加
            if sku_id in cart:
                # 存在累加个数
                origin_count = cart[sku_id]['count']
                count += origin_count
            cart[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 5.3返回响应
            response = Response(serializer.data)
            cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
            response.set_cookie('cart', cookie_cart, 24*3600)
            return response

    def get(self, request):
        """
        1.根据用户的信息进行判断
        2.登录用户获取redis数据
        3.未登录用户获取cookie数据
        4.查询商品数据,进行序列化操作
            4.1获取id信息   (数据结构必须 redis和cookie一致)
            4.2根据id信息查询商品(模型)
            4.3需要将模型转化为JSON
        """""

        # 1.根据用户的信息进行判断
        try:
            user = request.user
        except Exception:
            user = None
        if user and user.is_authenticated:
            # 2.登录用户获取redis数据
            # 2.1连接redis
            redis_conn = get_redis_connection('cart')
            # 2.2获取所有数据,从redis获取的数据是bytes类型
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            redis_selected_ids = redis_conn.smembers('cart_selected_%s' % user.id)
            # 2.3拼接成cookie的数据结构
            cart = {}
            for sku_id, count in redis_cart.items():
                cart[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_selected_ids
                }

        else:
            # 3.未登录用户获取cookie数据
            cart_str = request.COOKIES.get('cart')

            if cart_str:
                cart = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart = {}

        # 4.查询商品数据,进行序列化操作
        # 4.1获取id信息   (数据结构必须 redis和cookie一致)
        ids = cart.keys()
        # 4.2根据id信息查询商品(模型)
        skus = SKU.objects.filter(pk__in=ids)
        # skus中的SKU没有count和selected信息
        for sku in skus:
            sku.count = cart[sku.id]['count']
            sku.selected = cart[sku.id]['selected']
        # 4.3需要将模型转化为JSON
        serializer = CartSKUSerializer(skus, many=True)

        return Response(serializer.data)

    def put(self, request):
        """
        # 1.更新数据的提交和新增数据的提交是一样的,获取用户提交的数据,并且进行校验
        # 2.获取校验之后的数据
        # 3.获取用户信息,根据用户信息进行判断
        # 4.登录用户更新redis
        # 5.未登录用户更新cookie
        """""
        # 1.更新数据的提交和新增数据的提交是一样的,获取用户提交的数据,并且进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2.获取校验之后的数据
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        # 3.获取用户信息,根据用户信息进行判断
        try:
            user = request.user
        except Exception:
            user = None

        if user and user.is_authenticated:
            # 4.登录用户更新redis
            # 4.1连接redis
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 4.2更新数据,幂等操作,直接返回最终结果
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 是否选择
            if selected:
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            else:
                # 如果没有选中,应该把选中列表中可能存在的sku_id删除
                pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            # 4.3返回响应
            # 因为采用的是幂等,需要将服务器的最终结果返回回去
            return Response(serializer.data)
        else:
            # 5.未登录用户更新cookie
            # 5.1获取cookie数据进行判断
            cart_str = request.COOKIES.get('cart')
            if cart_str is not None:
                cart = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart = {}
            # 5.2更新数据
            # 因为是幂等操作,只要判断cart中是否有对应的商品id
            if sku_id in cart:
                cart[sku_id] = {
                    'count': count,
                    'selected': selected
                }
            # 5.3返回响应
            cookie_str = base64.b64encode(pickle.dumps(cart)).decode()

            response = Response(serializer.data)
            response.set_cookie('cart', cookie_str)

            return response

    def delete(self, request):
        """
        1.把要删除的商品id传递过来
        2.对id进行校验
        3.获取用户的状态进行判断
        4.登录用户操作redis
        5.非登录用户操作cookie
        """""
        # 1.把要删除的商品id传递过来
        # 2.对id进行校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')
        # 3.获取用户的状态进行判断
        try:
            user = request.user
        except Exception:
            user = None

        if user and user.is_authenticated:
            # 4.登录用户操作redis
            # 4.1连接redis
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 4.2操作数据
            pl.hdel('cart_%s'%user.id, sku_id)
            pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            # 4.3返回响应
            return Response(status=status.HTTP_204_NO_CONTENT)

        else:
            # 5.非登录用户操作cookie
            # 5.1获取cookie数据,判断是否存在
            cart_str = request.COOKIES.get('cart')

            if cart_str:
                cart = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart = {}

            response = Response(serializer.data)
            # 5.2删除数据
            if sku_id in cart:
                del cart[sku_id]
                # 组织数据
                cookie_str = base64.b64encode(pickle.dumps(cart)).decode()
                response.set_cookie('cart', cookie_str, 24*3600)

            # 5.3返回响应
            return response

